/**
  ******************************************************************************
  * @file    app_rtc.c
  * @author  tuancheng_Zero
  * @version V0.1
  * @date    08/25/2024
  * @brief   app_rtc
  ******************************************************************************
*/ 
#include <includes.h>
#include "time.h"

// 函数声明
void rtc_TimeAdjust(uint32_t timeStamp);

// 局部变量
static struct tm * timeInfo = NULL;
static RTC_DateType RTC_DateStructure = {0};
static RTC_TimeType RTC_TimeStructure = {0};


/// @brief 设定分频器
/// @param  none
static void rtc_PrescalerConfig(void){
    RTC_InitType RTC_InitStructure;

    RTC_InitStructure.RTC_SynchPrediv  = 0xff; //32.768KHz
    RTC_InitStructure.RTC_AsynchPrediv = 0x7f;
    RTC_InitStructure.RTC_HourFormat = RTC_24HOUR_FORMAT;
    RTC_Init(&RTC_InitStructure);
}

/// @brief 配置RTC
/// @param  none
static void rtc_Configuration(void){
    /* Enable PWR and BKP clocks */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);

    /* Allow access to BKP Domain */
    PWR_BackupAccessEnable(ENABLE);

    /* Reset Backup Domain */
    BKP_DeInit();

    /* Disable RTC clock */
    RCC_EnableRtcClk(DISABLE);

    /* Disable low speed internal clock */
    RCC_EnableLsi(DISABLE);

    /* Enable LSE */
    RCC_ConfigLse(RCC_LSE_ENABLE);

    /* Wait till LSE is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_LSERD) == RESET){
    }
    /* Select LSE as RTC Clock Source */
    RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);

    /* Enable RTC Clock */
    RCC_EnableRtcClk(ENABLE);

    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();
}

/// @brief RTC初始化
/// @param  none
void rtc_Initial(void){
    /* Enable PWR and BKP clocks */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);

    /* Allow access to BKP Domain */
    PWR_BackupAccessEnable(ENABLE);
    
    if (BKP_ReadBkpData(BKP_DAT1) != 0xA5A5){
        /* RTC Configuration */
        rtc_Configuration();

        /* prescaler Configuration */
        rtc_PrescalerConfig();
        
        /* Adjust time values */
        rtc_TimeAdjust(1704038400); // 2024/1/1 00:00:00

        BKP_WriteBkpData(BKP_DAT1, 0xA5A5);
    }
}

/// @brief 计算星期   蔡勒（Zeller）公式
/// @param timeData 时间数据
/// @return 周数
static uint8_t rtc_CalculateWeek(struct tm * timeData){
    uint16_t year = 0, month = 0, days = 0;

    month = timeData->tm_mon + 1;
    year = timeData->tm_year + 1900;
    days = timeData->tm_mday;
    if (month == 1 || month == 2)//判断month是否为1或2　
        year--, month += 12;
    int32_t c = year / 100;
    int32_t y = year - c * 100;
    int32_t week = y + y / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + days - 1;
    while (week < 0)
        week += 7;
    week %= 7;

    return (uint8_t)week;
}

/// @brief 时间戳转日期数据
/// @param timeValue 时间戳
static void rtc_TimeStampToDate(uint32_t timeValue){
    time_t timeStamp = timeValue;
    
    timeInfo = localtime(&timeStamp);
    RTC_DateStructure.Year = timeInfo->tm_year - 100;
    RTC_DateStructure.Month = timeInfo->tm_mon + 1;
    RTC_DateStructure.Date = timeInfo->tm_mday;
    RTC_DateStructure.WeekDay = rtc_CalculateWeek(timeInfo);

    RTC_TimeStructure.H12 = RTC_AM_H12;
    RTC_TimeStructure.Hours = timeInfo->tm_hour;
    RTC_TimeStructure.Minutes = timeInfo->tm_min;
    RTC_TimeStructure.Seconds = timeInfo->tm_sec;
}

/// @brief 获取实时时钟时间戳
/// @param  none
/// @return RTC时间戳
uint32_t rtc_GetRtcTimeStamp(void){
    uint32_t timeStamp = 0;
    struct tm timeData = {0};

    // 获取RTC日期数据
    RTC_GetDate(RTC_FORMAT_BIN, &RTC_DateStructure);
    RTC_GetTime(RTC_FORMAT_BIN, &RTC_TimeStructure);
    timeData.tm_year = RTC_DateStructure.Year + 100;
    timeData.tm_mon  = RTC_DateStructure.Month - 1;
    timeData.tm_mday = RTC_DateStructure.Date;
    timeData.tm_hour = RTC_TimeStructure.Hours;
    timeData.tm_min  = RTC_TimeStructure.Minutes;
    timeData.tm_sec =  RTC_TimeStructure.Seconds;

    share_RealTimeData_ckp.tm_year  = RTC_DateStructure.Year;
    share_RealTimeData_ckp.tm_mon   = RTC_DateStructure.Month ;
    share_RealTimeData_ckp.tm_mday  = RTC_DateStructure.Date;
    share_RealTimeData_ckp.tm_hour  = RTC_TimeStructure.Hours+8;
    share_RealTimeData_ckp.tm_min   = RTC_TimeStructure.Minutes ;
    share_RealTimeData_ckp.tm_sec   = RTC_TimeStructure.Seconds ;
    //转换成时间戳
    timeStamp = mktime(&timeData);
    return timeStamp;
}

/// @brief 时间调整
/// @param timeStamp 时间戳
void rtc_TimeAdjust(uint32_t timeStamp){
    uint8_t os_err;
    // 等待信号量
    OSSemPend(App_Sem_RTCADJ, 0, &os_err);
    // 设置时间日期
    rtc_TimeStampToDate(timeStamp);
    RTC_SetDate(RTC_FORMAT_BIN, &RTC_DateStructure);
    RTC_ConfigTime(RTC_FORMAT_BIN, &RTC_TimeStructure);
    // 释放信号量
    OSSemPost(App_Sem_RTCADJ);
}
